package org.base.admin.modules.auth;

import java.util.Objects;
import javax.servlet.http.HttpServletRequest;

import org.base.admin.common.exception.AuthenticationException;
import org.base.admin.config.security.JwtTokenUtil;
import org.base.admin.config.security.JwtUser;
import org.base.admin.config.security.WebSecurityConfig;
import org.base.common.R;
import org.base.common.ResponseCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AuthenticationController {

    private String tokenHeader = WebSecurityConfig.TOKEN_HEADER;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    @Qualifier("jwtUserDetailsService")
    private UserDetailsService userDetailsService;

    @PostMapping(WebSecurityConfig.AUTHENTICATION_PATH)
    public R createAuthenticationToken(@RequestBody JwtAuthenticationRequest authenticationRequest){

        try {
			authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
		} catch (UsernameNotFoundException e) {
			return new R(ResponseCode.USER_PASSWORD_WRONG);
		} catch (DisabledException e) {
            return new R(ResponseCode.USER_DISABLE);
        } catch (BadCredentialsException e) {
           return new R(ResponseCode.USER_PASSWORD_WRONG);
        }

        // Reload password post-security so we can generate the token
        final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
        final String token = jwtTokenUtil.generateToken(userDetails);

        // Return the token
        return R.result(new JwtAuthenticationResponse(token));
    }

    @GetMapping(WebSecurityConfig.AUTHENTICATION_REFRESH)
    public R refreshAndGetAuthenticationToken(HttpServletRequest request) {
        String authToken = request.getHeader(tokenHeader);
        final String token = authToken.substring(7);
        String username = jwtTokenUtil.getUsernameFromToken(token);
        JwtUser user = (JwtUser) userDetailsService.loadUserByUsername(username);

        if (jwtTokenUtil.canTokenBeRefreshed(token, user.getLastPasswordResetDate())) {
            String refreshedToken = jwtTokenUtil.refreshToken(token);
            return R.result(new JwtAuthenticationResponse(refreshedToken));
        } else {
            return new R(ResponseCode.TOKEN_NOT_ALLOW_REFRESH);
        }
    }

    /**
     * Authenticates the user. If something is wrong, an {@link AuthenticationException} will be thrown
     */
    private void authenticate(String username, String password) {
        Objects.requireNonNull(username);
        Objects.requireNonNull(password);
        authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
    }
    
    
}
